//
//    This is from http://elinux.org/RPi_Low-level_peripherals
//    15-January-2012
//    Dom  and  Gert (Thx!)

#include  <stdio.h>
#include  <string.h>
#include  <stdlib.h>
#include  <dirent.h>
#include  <fcntl.h>
#include  <assert.h>
#include  <sys/mman.h>
#include  <sys/types.h>
#include  <sys/stat.h>
#include  <unistd.h>

//  Access  from  ARM  Running  Linux
#define  BCM2708_PERI_BASE   0x20000000
#define  GPIO_BASE           (BCM2708_PERI_BASE  +  0x200000)  /*  GPIO  controller  */
#define  PAGE_SIZE           (4*1024)
#define  BLOCK_SIZE          (4*1024)

int    mem_fd;
char  *gpio_mem,  *gpio_map;
char  *spi0_mem,  *spi0_map;

//  I/O  access
volatile  unsigned  *gpio;

//  GPIO  setup  macros.  Always  use  INP_GPIO(x)  before  using  OUT_GPIO(x)  or  SET_GPIO_ALT(x,y)
#define  INP_GPIO(g)  *(gpio+((g)/10))  &=  ~(7<<(((g)%10)*3))
#define  OUT_GPIO(g)  *(gpio+((g)/10))  |=    (1<<(((g)%10)*3))
#define  SET_GPIO_ALT(g,a)  *(gpio+(((g)/10)))  |=  (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))

#define  GPIO_SET  *(gpio+7)    //  sets      bits  which  are  1  ignores  bits  which  are  0
#define  GPIO_CLR  *(gpio+10)  //  clears  bits  which  are  1  ignores  bits  which  are  0


/* ----------------------------------------------------------------------
*/
void gpioOutMode(unsigned port)
{
  INP_GPIO(port); // always set "INP" first..
  OUT_GPIO(port);
}

/* ----------------------------------------------------------------------
*/
void gpioSet(unsigned port, int value)
{
  if (value)
    GPIO_SET = 1<<port;
  else
    GPIO_CLR = 1<<port;
}

/* ----------------------------------------------------------------------
*/
void gpioSetMask(unsigned ports, int value)
{
  if (value)
    GPIO_SET = ports;
  else
    GPIO_CLR = ports;
}

/* ----------------------------------------------------------------------
*/
unsigned  gpioInit()
//
//  Set  up  a  memory  regions  to  access  GPIO
//
{
  /*  open  /dev/mem  */
  if  ((mem_fd  =  open("/dev/mem",  O_RDWR|O_SYNC)  )  <  0)  {
    fprintf(stderr,"can't  open  /dev/mem\n");
    return 1;
  }

  /*  mmap  GPIO  */

  //  Allocate  MAP  block
  if  ((gpio_mem  =  (char*)malloc(BLOCK_SIZE  +  (PAGE_SIZE-1)))  ==  NULL)  {
    fprintf(stderr,"allocation  error\n");
    return 2;
  }

  //  Make  sure  pointer  is  on  4K  boundary
  if  ((unsigned  long)gpio_mem  %  PAGE_SIZE)
    gpio_mem  +=  PAGE_SIZE  -  ((unsigned  long)gpio_mem  %  PAGE_SIZE);

  //  Now  map  it
  gpio_map  =  (char*)mmap(
            (caddr_t)gpio_mem,
            BLOCK_SIZE,
            PROT_READ|PROT_WRITE,
            MAP_SHARED|MAP_FIXED,
            mem_fd,
            GPIO_BASE
            );

  if  ((long)gpio_map  <  0)  {
    fprintf(stderr,"mmap  error  %d\n",  (int)gpio_map);
    return 3;
  }

  //  Always  use  volatile  pointer!
  gpio  =  (volatile  unsigned  *)gpio_map;

  return 0; // no errors..
}



